package logic

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"gitee.com/Cauchy_AQ/vxopen/model/redis"
	"gitee.com/Cauchy_AQ/vxopen/utils"
	"io"
	"net/http"
	"net/url"

	"gitee.com/Cauchy_AQ/vxopen/rpc/wechat/internal/svc"
	"gitee.com/Cauchy_AQ/vxopen/rpc/wechat/wechat"

	"github.com/zeromicro/go-zero/core/logx"
)

const (
	// API_VX_TICKET 获取 ticket 的 url
	API_VX_TICKET = "https://api.weixin.qq.com/cgi-bin/qrcode/create"
	// API_VX_QRCODE 获取 qrcode 的 url
	API_VX_QRCODE = "https://mp.weixin.qq.com/cgi-bin/showqrcode"
)

const (
	// ExpireQRCodeDefault 二维码 ticket 的默认过期时间 60s
	ExpireQRCodeDefault = 60
	// SceneIDQRCodeDefault 默认 scene_id 1
	SceneIDQRCodeDefault = 1
)

type QRCodeType = string

const (
	QR_TMP_NUMBER QRCodeType = "QR_SCENE"
	QR_TMP_STRING QRCodeType = "QR_STR_SCENE"
	QR_PER_NUMBER QRCodeType = "QR_LIMIT_SCENE"
	QR_PER_STRING QRCodeType = "QR_LIMIT_STR_SCENE"
)

var (
	// DefaultWechatTicketReq 默认二维码配置
	DefaultWechatTicketReq = WechatQRCodeParam{
		ExpireSeconds: ExpireQRCodeDefault,
		ActionName:    QR_TMP_NUMBER,
		ActionInfo: WechatQRCodeInfo{
			SceneId: SceneIDQRCodeDefault,
		},
	}
)

type (
	WechatQRCodeInfo struct {
		SceneId  int32  `json:"scene_id"`
		SceneStr string `json:"scene_str"`
	}
	WechatQRCodeParam struct {
		ExpireSeconds int32      `json:"expire_seconds"`
		ActionName    QRCodeType `json:"action_name"`
		ActionInfo    WechatQRCodeInfo
	}
	WechatTicketReq struct {
		AccessToken string `json:"access_token"`
		WechatQRCodeParam
	}
	WechatTicketResp struct {
		Ticket        string `json:"ticket"`
		ExpireSeconds int64  `json:"expire_seconds"`
		Url           string `json:"url"`
	}
)

type QrcodeLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewQrcodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QrcodeLogic {
	return &QrcodeLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *QrcodeLogic) Qrcode(in *wechat.QRCodeReq) (*wechat.QRCodeResp, error) {
	aToken, err := redis.GetAvailableAToken(l.svcCtx.Config.ModelConfig)
	if err != nil {
		return nil, err
	}
	resp2, err := getQRCodeTicket(&WechatTicketReq{
		AccessToken:       aToken,
		WechatQRCodeParam: DefaultWechatTicketReq,
	})
	if err != nil {
		return nil, err
	}
	image, imageUrl, err := getQRCode(resp2.Ticket)
	if err != nil {
		return nil, err
	}

	return &wechat.QRCodeResp{
		Image:    image,
		ImageURL: imageUrl,
		Ticket:   resp2.Ticket,
	}, nil
}

// https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=urlencode(xxx)
func getQRCode(ticket string) ([]byte, string, error) {
	ticket = url.QueryEscape(ticket)
	urls := fmt.Sprintf("%s?ticket=%s", API_VX_QRCODE, ticket)
	imageBody, err := utils.GetHttpBody(urls)
	return imageBody, urls, err
}

// https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=xxx
// https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html
func getQRCodeTicket(req *WechatTicketReq) (*WechatTicketResp, error) {
	urls := fmt.Sprintf("%s?access_token=%s", API_VX_TICKET, req.AccessToken)
	jsonEncodeData, err := json.Marshal(req.WechatQRCodeParam)
	if err != nil {
		return nil, err
	}
	realReq, err := http.NewRequest(http.MethodPost, urls, bytes.NewBuffer(jsonEncodeData))
	if err != nil {
		return nil, err
	}
	realReq.Header.Set("Content-Type", "application/json")
	c := &http.Client{}
	resp, err := c.Do(realReq)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	w := &WechatTicketResp{}
	err = json.Unmarshal(body, w)
	if err != nil {
		return nil, err
	}
	return w, nil
}
